/**
 *    Copyright 2009-2020 the original author or authors.
 *
 *    Licensed under the Apache License, Version 2.0 (the "License");
 *    you may not use this file except in compliance with the License.
 *    You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 *    Unless required by applicable law or agreed to in writing, software
 *    distributed under the License is distributed on an "AS IS" BASIS,
 *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *    See the License for the specific language governing permissions and
 *    limitations under the License.
 */
package org.apache.ibatis.aapl.util;

import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.plugin.Interceptor;
import org.apache.ibatis.scripting.defaults.DefaultParameterHandler;
import sun.reflect.generics.reflectiveObjects.ParameterizedTypeImpl;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Type;
import java.util.List;

/**
 * @author gu
 * @date 2020/3/25 12:24
 */
public class ReflectUtil {
  public static String getClassByMapperId(String id) {
    String[] split = id.split("\\.");
    StringBuilder builder = new StringBuilder();
    builder.append(split[0]);
    for (int i = 1; i < split.length - 1; i++) {
      builder.append(".");
      builder.append(split[i]);
    }
    return builder.toString();
  }

  public static String getMethodNameByMapperId(String id) {
    String[] split = id.split("\\.");
    return split[split.length - 1];
  }

  public static Method getMethodByFullName(String id) {
    String clazzName = getClassByMapperId(id);
    String methodName = getMethodNameByMapperId(id);
    try {
      Class<?> clazz = Class.forName(clazzName);
      Method[] methods = clazz.getMethods();
      for (Method method : methods) {
        if (method.getName().equals(methodName)) {
          return method;
        }
      }
    } catch (ClassNotFoundException e) {
      e.printStackTrace();
      return null;
    }
    return null;
  }

  public static Method getSetIdMethod(Object o) {
    try {
      return o.getClass().getMethod("setId", Integer.class);
    } catch (NoSuchMethodException e) {
      return null;
    }
  }

  /**
   * TODO 反复调用 需要添加缓存
   */
  public static Method getSetMethod(Object o, String field, Class<?> type) {
    String methodName = NameUtil.getSetterNameByField(field);
    try {
      return o.getClass().getMethod(methodName, type);
    } catch (NoSuchMethodException e) {
      e.printStackTrace();
    }
    return null;
  }

  public static Method getGetMethod(Object o, String field) {
    String methodName = NameUtil.getGetterNameByField(field);
    try {
      return o.getClass().getMethod(methodName);
    } catch (NoSuchMethodException e) {
      e.printStackTrace();
    }
    return null;
  }

  public static Field getFieldByName(Object obj, String name) {
    try {
      Field declaredField = obj.getClass().getDeclaredField(name);
      return declaredField;
    } catch (NoSuchFieldException e) {
      e.printStackTrace();
    }
    return null;
  }

  public static Method getSetMethod(Object o, String field) {
    Field fieldByName = getFieldByName(o, field);
    return getSetMethod(o, field, fieldByName.getType());
  }

  public static Class<?> getMethodReturnType(Method method) {
    Class<?> returnType = method.getReturnType();

    if (returnType.isAssignableFrom(List.class)) {
      Type actualTypeArgument = ((ParameterizedTypeImpl) method.getGenericReturnType()).getActualTypeArguments()[0];
      returnType = (Class<?>) actualTypeArgument;
    }
    return returnType;
  }
}
